<?php
/**
 * User: day
 * Date: 2018/08/11
 * Time: 17:15
 * Description: 活动类基类
 */

namespace App\Console\Commands\Activity;

use App\Console\Base;

use App\Models\ActiveRecord;
use App\Models\Message;
use App\Services\ActivityService;
use App\Services\MoneyService;
use common\enum\ActiveEnum;
use common\enum\CommonEnum;
use common\enum\MoneyLogEnum;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Carbon;
use App\Services\UserService;
use Illuminate\Support\Facades\Log;

abstract class BaseActivity  extends Base {
    const AUTH_ACTIVITY = 'is_refuse_coupon';
    const AUTH_REBET = 'is_refuse_rebate';
    const STATUS_SENDING = 3;//发布中
    const STATUS_SENDED = 1;//发布中
    const MESSAGE_TYPE = 1;//重要消息
    const SEND_TYPE_CUSTOM =3; //自定义
    const IS_COUPON = 1;   //优惠活动
    // active_template 里的模板ID
    public $templateID;
    // 活动的名字
    public $activityName;
    // 第一个满足条件的活动？？
    public $rule;
    // rule 活动对应的游戏
    public $games;
    public $hall_id;
    public $activityKey;

    // 是否修复
    public $prizeRepair;

    /**
     * 程序入口
     */
    public function handle($args=null)
    {
        $this->info($this->activityName . " 开始处理");
        $hall_id = $this->hall_id = $this->option('hall_id');
        $prizeRepair=$this->prizeRepair = $this->option('prizeRepair')??0;
        $redis = Redis::connection ();
        $curTime = Carbon::now();
        $date = Carbon::now()->toDateString();
        try {
            $list = $this->getRule ();
            foreach ($list as $item){
                $activityKey = $this->activityKey.":". $item['id'];
                $this->info('本次活动=key：'.$activityKey . "统计开始");
                $this->info($activityKey. "开始统计了");
                $this->rule = $item;
                $this->rule['rule'] = json_decode($this->rule['rule'], true);
                $this->games = $this->rule['rule']['game']??[];

                //判断活动是否过期，过期先设置为未发放状态，发放完毕时设置为停用
                $is_sending = false; //是否未发放状态
                $this->info($activityKey. "当前时间".$curTime);
                $this->info($activityKey. "活动结束时间".$this->rule['end_time']);
                if (strtotime ($curTime) >= strtotime ($this->rule['end_time'])){
                    $this->info($activityKey. "状态改为待发放状态");
                    ActivityService::updateActiveStatus ($this->rule['id'],ActiveEnum::ACTIVE_STATUS_SENDING);
                    $this->info('本次活动=key：'.$activityKey . "状态已改为待派奖");
                    $task_begin = $date." 01:00"; //定时任务凌晨1点后执行
                    if ($curTime < $task_begin){
                        continue;
                    }
                    $is_sending = true;
                }
                $this->info($activityKey. "符合时间统计条件开始统计了");
                $activity_begin = $this->rule['begin_time'];
                $activity_end = $this->rule['end_time'];
                if (!$redis->get ($activityKey)){
                    $redis->set ($activityKey,$activity_begin);
                }
                $start = $redis->get ($activityKey);
                $start_date = Carbon::parse($start)->toDateString ();
                $start_time = Carbon::parse($start)->startOfDay ();
                $this->info($activityKey. "本次开始时间".$start);
                $this->info($activityKey. "本次统计开始时间".$start_time);
                $this->info($activityKey. "本次统计开始日期".$start_date);
                if (strtotime ($start) <= strtotime ($start_time)){
                    $start = $start_time;
                }
                if ($this->rule['rule']['issue_cycle'] == 'day'){
                    if ($date == $start_date) {
                        $this->info($this->activityName . " 处理完成");
                        continue;
                    }
                    $next_start = Carbon::parse($start)->addDays(1)->toDateString ();
                    $end = Carbon::parse($start)->endOfDay ();
                }elseif ($this->rule['rule']['issue_cycle'] == 'week'){
                    if ($date == $start_date) {
                        $this->info($this->activityName . " 处理完成");
                        continue;
                    }
                    $next_start = Carbon::parse($start)->addWeeks(1)->startOfWeek()->toDateString ();
                    $end = Carbon::parse($start)->endOfWeek ()->endOfDay ();
                }elseif ($this->rule['rule']['issue_cycle'] == 'month'){
                    if ($date == $start_date) {
                        $this->info($this->activityName . " 处理完成");
                        continue;
                    }
                    $next_start = Carbon::parse($start)->addMonths(1)->firstOfMonth()->toDateString ();
                    $end = Carbon::parse($start)->endOfMonth ()->endOfDay ();
                }elseif ($this->rule['rule']['issue_cycle'] == 'active'){
                    if ($date == $start_date) {
                        $this->info($this->activityName . " 处理完成");
                        continue;
                    }
                    $next_start = Carbon::parse($activity_end)->addDays(1)->toDateString ();
                    $end = Carbon::parse($activity_end)->endOfDay ();

                }else{
                    $next_start = $curTime;
                    $senddate = Carbon::parse($start)->toDateString ();
                    $end = $curTime;
                }
                if (strtotime ($activity_end) < strtotime ($end)){
                    $end = $activity_end;
                }

                if ( strtotime ($start) > strtotime ($end)){
                    continue;
                }
                $this->info($this->activityName.$item['id']."本次统计开始时间为".$start);
                $this->info($this->activityName.$item['id']."本次统计结束时间为".$end);
                $this->info($this->activityName.$item['id']."今日统计的日期为".$next_start);
                if ($date == Carbon::parse($next_start)->toDateString() || $prizeRepair){
                    $this->info($this->activityName.$item['id']."将统计数据，统计时间为".date("Y-m-d H:i:s"));
                    $this->info($activityKey. "将要统计数据了,当前时间为:".date("Y-m-d H:i:s"));
                    $this->statisticsData ($this->rule,$hall_id,$start,$end);
                }
                $redis->set ($activityKey,$next_start);
                $this->info($this->activityName.$item['id']."活动key{$this->activityKey}下次统计的时间".$next_start);

                $sendprize_delay_day = $this->rule['sendprize_delay_day']+1;
                //发送时间区分
                if(!isset($senddate)){
                    $senddate = Carbon::parse($curTime)->subDays($sendprize_delay_day)->toDateString ();
                }
                $this->info($activityKey. "发放奖励日期为:".$senddate);
                $sendprize_delay_time = $this->rule['sendprize_delay_time'];
                $sendtime = $date." ".$sendprize_delay_time;
                $this->info($this->activityName.$item['id']."发放延迟时间为".$sendprize_delay_day.'天'.$sendprize_delay_time);
                if (abs (strtotime ($curTime) - strtotime ($sendtime)) <= 60 || !$sendprize_delay_time || $prizeRepair){
                    $this->info('开始派发'.$this->activityName .$item['id']. "日期为:{$senddate}的奖励了");
//                    $data = $this->data($senddate);
//                    $this->info($this->activityName .$item['id']."本次派奖数量: ".count($data));
//                    $this->info($activityKey."发放奖励数量".is_array($data)?count($data):0);
//                    $this->sendPrize($data);
                    $this->info($senddate);
                    $this->sendPrizeByPage($senddate);
                    $this->info($this->activityName .$item['id']. "已派奖结束！");
                    //如果是未发放状态发放完毕改为停用
                    if($is_sending){
                        $this->info($this->activityName.$item['id']."已经结束统计,改为已停用状态");
                        $this->info($activityKey."修改状态为已停用");
                        ActivityService::updateActiveStatus ($this->rule['id'],ActiveEnum::ACTIVE_STATUS_UNENABLE);
                        $redis->del ($activityKey);
                    }
                }
            }
        } catch(\Throwable $e) {
            $msg = "请修复程序再运行:\nMessage:" . $e->getMessage() . "\nFile:" . $e->getFile() . "\nLine:" . $e->getLine() . "\ntrace: " . $e->getTraceAsString() . PHP_EOL;
            $this->error($msg);
        }
        $this->info($this->activityName . " 处理完成");
    }

    /**
     * 获取该时间满足的所有活动，以及活动对应的游戏
     */
    public function getRule()
    {
        $list = ActivityService::getActiveList($this->templateID,$this->hall_id);
        if (empty($list)) {
            return [];
        }
        return $list;
    }

    /**
     * 判断用户是否可参加优惠 或 返水
     * @param array $user
     * @param  string $type
     * @return bool
     */
    protected function checkUserAuth($user, string $type){
        $auth = explode(',', $user['auth_status']);
        return !in_array($type, $auth);
    }

    /**
     * 检查活动当前已发放的次数是否超过活动限制
     * @param int $userID 用户ID
     * @param int $activeID 活动ID
     * @param int $limitTimes 该活动限制发放的次数
     * @return bool
     */
    protected function checkSendTimes(int $userID, int $activeID,$limitTimes=null)
    {
        if (!$limitTimes) {
            return true;
        }
        $sendTimes = ActivityService::getCurSendTimes($userID, $activeID);
        return $limitTimes > $sendTimes;
    }

    /**
     * 活动派奖，发放优惠
     */
    public function sendPrize(array $data) {
        if (empty($data)){
            return;
        }
        $isAuto = $this->rule['is_auto_sendprize'] == 1;
        $trunks = array_chunk($data, 500);
        foreach ($trunks as $trunk) {
            DB::beginTransaction();
            try {
                foreach ($trunk as $record) {
                    // 目前派发都是算好的
                    $prize = $record['amount'];
                    $sendStatus = $record['status'];
                    $updateParams = ['status' => $sendStatus, 'amount' => $prize];
                    $userId = $record['user_id'];
                    $username = $record['username'];
                    $hallId = $record['hall_id'];
                    $orderNumber = generate_order_number ();
                    // 活动名称
                    $activeName = $record['active_name'];
                    if (!ActivityService::updateActiveRecord($record['id'], $updateParams)) {
                        continue;
                    }
                    // 更新为已派奖状态
                    if ($sendStatus == 1) {
                        if ($isAuto && $prize > 0) {
                            MoneyService::updateMoney($hallId, $userId, $username,
                                $orderNumber, MoneyLogEnum::USER_MONEY_INCOME, MoneyLogEnum::USER_DEPOSIT_ACTIVITY, 0, $prize,
                                0, 0, $activeName.'活动发放优惠');
                            //发送消息
                            $massage = [
                                'title'=> '活动奖金已到账',
                                'content' => '您满足'.$activeName.'活动规则，系统已将'.($prize/CommonEnum::MONEY_RATE).'元奖金,发放至您的账户上。可至【账变记录】中查看。',
                                'status' => self::STATUS_SENDED,
                                'hall_id' => $hallId,
                                'admin_user_id' => 0,
                                'admin_username' => "系统",
                                'send_type' => self::SEND_TYPE_CUSTOM,
                                'recipient' => $username
                            ];
                            if (!($id = DB::table('message')->insertGetId($massage))){
                                continue;
                            }

                            $paramArr = [
                                'type' => self::MESSAGE_TYPE,
                                'hall_id' => $hallId,
                                'recipient_id' => $userId,
                                'message_id' => $id
                            ];
                            if (!DB::table('message_record')->insert($paramArr)){
                                continue;
                            }
                        }
                    }
                }
                DB::commit();
            } catch(\Exception $e) {
                DB::rollBack();
                $msg = "请修复程序再运行:\nMessage:" . $e->getMessage() . "\nFile:" . $e->getFile() . "\nLine:" . $e->getLine() . "\ntrace: " . $e->getTraceAsString() . PHP_EOL;
                Log::error($msg);
            }
        }
    }

    /**
     * 活动派奖，分页发放优惠
     * @param $date
     */
    public function sendPrizeByPage($date) {
        // 检测是否参加活动，以及有效性
        $where = ['status' => ActiveEnum::ACTIVE_STATUS_UNSEND, 'template_id' => $this->templateID];
        if (!$this->prizeRepair) {
            $where['date'] = $date;
        }
        $count = ActiveRecord::where($where)->count();
        if($count>0){
            $size = 1000;
            for($i=1;$i<=ceil($count/$size);$i++){
                $records = ActiveRecord::select(['id', 'hall_id', 'user_id', 'active_id', 'active_name', 'username', 'amount','status'])
                    ->where($where)
                    ->offset(($i - 1) * $size)->limit($size)
                    ->get()->toArray();
                if(empty($records)){
                    continue ;
                }
                $res = [];
                // 检测是否参加活动，以及有效性
                foreach ($records as &$record) {
                    $result = $res[$record['user_id']] ?? [ 'ordernum' => 0 ];
                    $result['ordernum'] ++;
                    $res[$record['user_id']] = $result;
                    if ($this->rule['limit_times'] && $res[$record['user_id']]['ordernum'] > $this->rule['limit_times']){
                        continue;
                    }
                    // 检测是否超过发放次数
                    if (!$this->checkSendTimes($record['user_id'], $this->rule['id'], $this->rule['limit_times'])) {
                        if (!ActivityService::checkUserAuth($record['user_id'], self::AUTH_ACTIVITY)) {
                            $record['status'] = ActiveEnum::ACTIVE_STATUS_NORIGHT;
                        } else {
                            $record['status'] = ActiveEnum::ACTIVE_STATUS_SEND_MORE;
                        }
                    } else {
                        $record['status'] = ActiveEnum::ACTIVE_STATUS_SEND;
                    }
                }
                // 派奖
                $isAuto = $this->rule['is_auto_sendprize'] == 1;
                DB::beginTransaction();
                try {
                    foreach ($records as $record) {
                        // 目前派发都是算好的
                        $prize = $record['amount'];
                        $sendStatus = $record['status'];
                        $updateParams = ['status' => $sendStatus, 'amount' => $prize];
                        $userId = $record['user_id'];
                        $username = $record['username'];
                        $hallId = $record['hall_id'];
                        $orderNumber = generate_order_number ();
                        // 活动名称
                        $activeName = $record['active_name'];
                        if (!ActivityService::updateActiveRecord($record['id'], $updateParams)) {
                            continue;
                        }
                        // 更新为已派奖状态
                        if ($sendStatus == ActiveEnum::ACTIVE_STATUS_SEND) {
                            if ($isAuto && $prize > 0) {
                                MoneyService::updateMoney($hallId, $userId, $username,
                                    $orderNumber, MoneyLogEnum::USER_MONEY_INCOME, MoneyLogEnum::USER_DEPOSIT_ACTIVITY, 0, $prize,
                                    0, 0, $activeName.'活动发放优惠');
                                //发送消息
                                $massage = [
                                    'title'=> '活动奖金已到账',
                                    'content' => '您满足'.$activeName.'活动规则，系统已将'.($prize/CommonEnum::MONEY_RATE).'元奖金,发放至您的账户上。可至【账变记录】中查看。',
                                    'status' => self::STATUS_SENDED,
                                    'hall_id' => $hallId,
                                    'admin_user_id' => 0,
                                    'admin_username' => "系统",
                                    'send_type' => self::SEND_TYPE_CUSTOM,
                                    'recipient' => $username
                                ];
                                if (!($id = DB::table('message')->insertGetId($massage))){
                                    continue;
                                }
                                $paramArr = [
                                    'type' => self::MESSAGE_TYPE,
                                    'hall_id' => $hallId,
                                    'recipient_id' => $userId,
                                    'message_id' => $id
                                ];
                                if (!DB::table('message_record')->insert($paramArr)){
                                    continue;
                                }
                            }
                        }
                    }
                    DB::commit();
                } catch(\Exception $e) {
                    DB::rollBack();
                    $msg = "请修复程序再运行:\nMessage:" . $e->getMessage() . "\nFile:" . $e->getFile() . "\nLine:" . $e->getLine() . "\ntrace: " . $e->getTraceAsString() . PHP_EOL;
                    Log::error($msg);
                }
            }
        }
    }


    /**
     * 获取满足活动条件的数据
     */
    public function data($date,$userId = null)
    {
        // 检测是否参加活动，以及有效性
        $records = ActivityService::checkIfPacitipate($userId, $date, $this->templateID,$this->prizeRepair);
        if (empty($records)) {
            return [];
        }
        $res = [];
        foreach ($records as &$record) {
            $result = $res[$record['user_id']] ?? [ 'ordernum' => 0 ];
            $result['ordernum'] ++;
            $res[$record['user_id']] = $result;
            if ($this->rule['limit_times'] && $res[$record['user_id']]['ordernum'] > $this->rule['limit_times']){
                continue;
            }
            // 检测是否超过发放次数
            if (!$this->checkSendTimes($record['user_id'], $this->rule['id'], $this->rule['limit_times'])) {
                if (!ActivityService::checkUserAuth($record['user_id'], self::AUTH_ACTIVITY)) {
                    $record['status'] = ActiveEnum::ACTIVE_STATUS_NORIGHT;
                } else {
                    $record['status'] = ActiveEnum::ACTIVE_STATUS_SEND_MORE;
                }
            } else {
                $record['status'] = ActiveEnum::ACTIVE_STATUS_SEND;
            }
        }
        return $records;
    }
    /**
     * 获取需要派奖的数据
     */
    public abstract function statisticsData($activity,$hall_id,$start,$end);
}